/*
  Computergrafik I, WiSe 2010/2011 (Programmrahmen Version 1)
  Blatt ?, Aufgabe ?
*/

#include "../common/cg_common.h"
#include <time.h>
#include <iostream>
#include <QImage>
#include <QGLWidget>

int initWinWidth, initWinHeight;
int winWidth = initWinWidth = 500;
int winHeight = initWinHeight = 500;
clock_t cpuTime = -1;
bool upPressed = false;
bool leftPressed = false;
bool rightPressed = false;

using namespace std;

struct AnimationConstraints {
    GLuint* textures;
    int currentTexture;
    clock_t timeOfLastChange;
    int timeBetweenChanges;
    int currentTimeDiff;
    bool drawLeft;
};

struct Figur {
    int type;
    int x;
    int y;
    int width;
    int height;
    double direction[3];
    AnimationConstraints constraints;
};

Figur* player;

void initGL(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    glutInitWindowSize(winWidth, winHeight);
    glutCreateWindow("Run and Jump");

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    /*
    GLfloat mat_specular[] = {0.02, 0.02, 0.02, 1.0};
    GLfloat mat_shininess[] = {200.0};
    GLfloat mat_ambient[] = {0.4, 0.3, 0.3, 1.0};
    GLfloat mat_diffuse[] = {0.3, 0.6, 0.3, 1.0};

    GLfloat light_ambient[] = {0.1, 0.1, 0.1, 1.0};
    GLfloat light_diffuse[] = {1, 1, 1, 1.0};
    GLfloat light_position[] = {10.0, 10.0, 10.0, 1.0};

    glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);

    glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);*/
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_TEXTURE_2D);
}

void drawPlayer() {
    //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    int left;
    player->constraints.drawLeft ? left = 1 : left = 0;

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[player->constraints.currentTexture]);
    glBegin(GL_QUADS);
        glTexCoord2d(0.0 + left, 0); glVertex2d(player->x, player->y);
        glTexCoord2d(1.0 - left, 0); glVertex2d(player->x + player->width, player->y);
        glTexCoord2d(1.0 - left, 1); glVertex2d(player->x + player->width, player->y + player->height);
        glTexCoord2d(0.0 + left, 1); glVertex2d(player->x, player->y + player->height);
    glEnd();
    //glDisable(GL_TEXTURE_2D);
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, initWinWidth, 0.0, initWinHeight);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    drawPlayer();

    glutSwapBuffers();
}

void reshape(int width, int height) {
    glViewport(0, 0, width, height);
    winWidth = width;
    winHeight = height;
}

void keyPressed(unsigned char k, int x, int y) {
    switch(k) {
        case 27: // Escape
            exit(EXIT_SUCCESS);

        case 'w':
            upPressed = true;
            break;

        case 'a':
            leftPressed = true;
            break;

        case 'd':
            rightPressed = true;
            break;
    }
}

void keyReleased(unsigned char k, int x, int y) {
    switch(k) {
        case 'w':
            upPressed = false;
            break;

        case 'a':
            leftPressed = false;
            break;

        case 'd':
            rightPressed = false;
            break;
    }
}

bool playerOnGround() {
    return player->y <= 0;
}

void frame(clock_t timeDiff) {
    // Check for pressed buttons
    if(upPressed) {
        if(playerOnGround()) {
            // Jump!
            player->direction[1] = 1.0;
        }
    }
    if(leftPressed && !rightPressed) {
        // Left
        player->direction[0] = -1.0;
        if(player->constraints.currentTexture == 0 || !player->constraints.drawLeft) {
            player->constraints.drawLeft = true;
            player->constraints.currentTexture = 1;
        } else {
            player->constraints.currentTimeDiff += timeDiff;
            if(player->constraints.currentTimeDiff > player->constraints.timeBetweenChanges) {
                player->constraints.currentTimeDiff -= player->constraints.timeBetweenChanges;
                player->constraints.currentTexture != 6 ? player->constraints.currentTexture++ : player->constraints.currentTexture = 1;
            }
        }
        if(!playerOnGround()) {
            player->constraints.currentTexture = 3;
        }
    } else if(!leftPressed && rightPressed) {
        // Right
        player->direction[0] = 1.0;
        if(player->constraints.currentTexture == 0 || player->constraints.drawLeft) {
            player->constraints.drawLeft = false;
            player->constraints.currentTexture = 1;
        } else {
            player->constraints.currentTimeDiff += timeDiff;
            if(player->constraints.currentTimeDiff > player->constraints.timeBetweenChanges) {
                player->constraints.currentTimeDiff -= player->constraints.timeBetweenChanges;
                player->constraints.currentTexture != 6 ? player->constraints.currentTexture++ : player->constraints.currentTexture = 1;
            }
        }
        if(!playerOnGround()) {
            player->constraints.currentTexture = 3;
        }
    } else {
        // Neither left nor right
        player->direction[0] = 0.0;
        if(playerOnGround()) {
            player->constraints.currentTexture = 0;
        } else {
            player->constraints.currentTexture = 3;
        }
    }

    // Change player's position
    double movementFactor = 5.0;
    if(player->direction[0] != 0) { // X
        player->x += player->direction[0] * movementFactor;
    }
    if(player->direction[1] != 0) { // Y
        player->y += player->direction[1] * movementFactor;
    }

    // Check player's position
    if(player->x < 0) {
        // X left
        player->x = 0;

    } else if(player->x > initWinWidth - player->width) {
        // X right
        player->x = initWinWidth - player->width;
    }
    if(playerOnGround()) {
        // Y
        player->y = 0;
    }

    // Modify player direction
    if(playerOnGround()){
        // On ground
        if(player->direction[1] < 0) { // Down
            player->direction[1] = 0.0;
        }
    } else {

        //Mid-air
        if(abs(player->direction[1]) < 1 / movementFactor) {
            player->direction[1] = - ((1 / movementFactor) + 0.1) ;
        } else if(player->direction[1] < 0) { // Down
            player->direction[1] *= 1.2;
        } else if(player->direction[1] > 0) { // Up
            player->direction[1] *= 0.8;
        }
    }
}

void timer(int value) {
    clock_t currentCpuTime = clock();

    if(cpuTime != -1) {
        clock_t diff = currentCpuTime - cpuTime;
        frame(diff);
        glutPostRedisplay();
    }
    cpuTime = currentCpuTime;
    glutTimerFunc(30, timer, 0);
}

void initPlayerTextures() {
    player->constraints.textures = new GLuint[7];
    glGenTextures(7, player->constraints.textures);

    // Read player sprites
    QImage multiSprite;
    if(!multiSprite.load("guybrushSprite.png")) {
        cout << "guybrushSprite.png konnte nicht geladen werden" << endl;
        return;
    }

    // Bind motionless texture
    QImage motionless = multiSprite.copy(0, 0, 205, 300);
    motionless = QGLWidget::convertToGLFormat(motionless);

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[0]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, motionless.width(), motionless.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, motionless.bits());

    // Bind step 0 texture
    QImage step0 = multiSprite.copy(205, 0, 205, 300);
    step0 = QGLWidget::convertToGLFormat(step0);

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[1]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, step0.width(), step0.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, step0.bits());

    // Bind step 1 texture
    QImage step1 = multiSprite.copy(410, 0, 205, 300);
    step1 = QGLWidget::convertToGLFormat(step1);

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[2]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, step1.width(), step1.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, step1.bits());

    // Bind step 2 texture
    QImage step2 = multiSprite.copy(615, 0, 205, 300);
    step2 = QGLWidget::convertToGLFormat(step2);

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[3]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, step2.width(), step2.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, step2.bits());

    // Bind step 3 texture
    QImage step3 = multiSprite.copy(820, 0, 205, 300);
    step3 = QGLWidget::convertToGLFormat(step3);

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[4]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, step3.width(), step3.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, step3.bits());

    // Bind step 4 texture
    QImage step4 = multiSprite.copy(1025, 0, 205, 300);
    step4 = QGLWidget::convertToGLFormat(step4);

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[5]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, step4.width(), step4.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, step4.bits());

    // Bind step 5 texture
    QImage step5 = multiSprite.copy(1230, 0, 205, 300);
    step5 = QGLWidget::convertToGLFormat(step5);

    glBindTexture(GL_TEXTURE_2D, player->constraints.textures[6]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, step5.width(), step5.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, step5.bits());
}

int main(int argc, char** argv) {
    player = new Figur;
    player->width = 40;
    player->height = 60;
    player->x = (winWidth - player->width) / 2;
    player->y = 0;
    player->type = 1;
    player->direction[0] = 0.0;
    player->direction[1] = 0.0;
    player->direction[2] = 0.0;
    player->constraints.timeBetweenChanges = 100;




    initGL(argc, argv);
initPlayerTextures();
    // GLUT callbacks
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyPressed);
    glutKeyboardUpFunc(keyReleased);
    glutTimerFunc(0, timer, 0);

    glutMainLoop();
}
